<script setup lang="ts">
import type { ElTable } from 'element-plus'
import { nextTick, ref } from 'vue'
const props = defineProps({
  columns: {
    type: Array,
    default: () => []
  },
  indexShow: {
    type: Boolean,
    default: false
  }
})

const table = ref<InstanceType<typeof ElTable>>()
const editRow = ref<any>(null) // 当前编辑行
const deleted = ref<any>([]) // 删除行
const data = ref<any>([]) // 表格实例
const multipleSelection = ref<any>([]) // 多选
const indexMethod = (index: number) => {
  return index * 1 + 1
}

const loadData = (dataInfo: any) => {
  editRow.value = null //重置编辑行
  deleted.value = [] //重置删除列表
  data.value = dataInfo
} // 开始编辑

const valChange = (val: any, row: any, data: any, col: any) => {
  if (col.editor.onchange) col.editor.onchange(val, row, data, col)
}

const rowStringify = (row: any) => {
  return JSON.stringify(
    props.columns.reduce((r: any, i: any) => {
      let val = row[i.prop]
      if (i.editor.type == 'number') val = val != null && val != '' ? Number(val) : null //数值型转换
      r[i.prop] = val
      return r
    }, {})
  )
}

const endEdit = () => {
  if (!editRow.value) return true //无编辑行
  if (
    props.columns.find(
      (i: any) => i.editor.validator && !i.editor.validator(editRow.value, data.value, i)
    )
  ) {
    table.value!.setCurrentRow(editRow.value) //校验不通过，留在原编辑行
    return false
  } else {
    editRow.value.isEdit = false //结束编辑
    if (editRow.value.mode != 'insert')
      editRow.value.mode = editRow.value.ori != rowStringify(editRow.value) ? 'updated' : null //标识数据是否变更
    editRow.value = null
    return true
  }
}

const rowClick = (row: any) => {
  if (row == editRow.value) return //就是当前编辑行
  if (!endEdit()) return //编辑行校验不通过，无法结束
  if (!row.ori) row.ori = rowStringify(row) //记录原始值
  // this.$set(row, 'isEdit', true) //开启编辑
  row.isEdit = true
  editRow.value = row //记录当前编辑行
}

const handleSelectionChange = (val: any) => {
  multipleSelection.value = val
}
const addRow = (row: any = {}) => {
  if (!endEdit()) return //编辑行校验不通过，无法结束
  props.columns.forEach((i: any) => row[i.prop] == undefined && (row[i.prop] = null)) //编辑行如未设值则默认给空值
  data.value.push((editRow.value = { ...row, mode: 'insert', isEdit: true })) //添加行并开启编辑
  nextTick(() => table.value!.setCurrentRow(editRow.value))
}
const removeSelected = () => {
  multipleSelection.value.forEach((i: any) => {
    if (i == editRow.value) editRow.value = null //是当前编辑行，则重置编辑行
    if (i.mode != 'insert') deleted.value.push(i) //记录删除行
    data.value.splice(data.value.indexOf(i), 1) //删除行
  })
}
const getChanges = () => {
  if (!endEdit()) return false //编辑行校验不通过，无法结束
  table.value?.setCurrentRow(null)
  return data.value.reduce(
    (r: any, i: any) => {
      i.mode && r[i.mode].push(i)
      return r
    },
    { insert: [], updated: [], deleted: deleted.value }
  )
}

defineExpose({
  data,
  editRow,
  valChange,
  endEdit,
  loadData,
  addRow,
  removeSelected,
  getChanges
})
</script>
<template>
  <el-table
    ref="table"
    class="editTable"
    :data="data"
    stripe
    border
    @row-click="rowClick"
    @selection-change="handleSelectionChange"
  >
    <el-table-column type="selection" align="center" width="40" />
    <el-table-column type="index" v-if="indexShow" :index="indexMethod" align="center" width="40" />
    <slot name="prepend" />
    <el-table-column v-for="(col, i) in columns" :key="i" v-bind="col">
      <template v-slot="{ row }: any">
        <span v-if="!row.isEdit">{{
          col?.editor.dict
            ? (col.editor.dict.find((o: any) => o.id == row[col.prop]) || {}).name
            : row[col.prop]
        }}</span>
        <!-- 只读 -->
        <el-input
          v-else-if="!col.editor.dict"
          v-model="row[col.prop]"
          :type="col.editor.type || 'text'"
          size="small"
          @change="(v: any) => valChange(v, row, data, col)"
          :class="{
            invalid: (col.editor.validator && !col.editor.validator(row, data, col)) || false
          }"
        />
        <el-select
          v-else
          v-model="row[col.prop]"
          size="small"
          clearable
          filterable
          @change="(v: any) => valChange(v, row, data, col)"
          :class="{
            'w-100': true,
            invalid: (col.editor.validator && !col.editor.validator(row, data, col)) || false
          }"
        >
          <el-option
            v-for="item in col.editor.dict"
            :key="item.id"
            :value="item.id"
            :label="item.name"
          />
        </el-select>
      </template>
    </el-table-column>
    <slot name="append" />
  </el-table>
</template>
<style scoped lang="less">
.editTable {
  height: calc(80vh - 200px);
  .el-table__cell {
    padding-top: 5px !important;
    padding-bottom: 5px !important;

    .cell {
      height: 32px !important;
      line-height: 32px !important;

      .el-input__inner {
        padding-left: 5px !important;
        padding-right: 5px !important;
      }

      .invalid .el-input__inner {
        border-color: #f56c6c !important;
      }
    }
  }

  .current-row > .el-table__cell {
    background-color: #9bdeff !important;
  }
}
</style>
